﻿using System;
using System.Threading;
using Liber;
using Liber.ORM;
using Liber.SOA;

namespace FlashQ
{
    public class DemoApp : SOAUnit
    {
        [API]
        FnResult<DyObject> CheckIn(SOARequestContext rq)
        {
            string key = rq.GetOrDefault<string>("key", "");
            string pwd = rq.GetOrDefault<string>("password", "");
            string opnsrc = rq.GetOrDefault<string>("opensrc", "");
            string captcha = rq.GetOrDefault<string>("captcha", "");

            var cmd = PG().Select<Account>().As("n")
                        .Where("acnt").Eq(key)
                        .And("pass").Eq((pwd + "_FlashQ").MD5Encrypt_normal())
                        .Limit(1);

            var ants = cmd.Query();
            if (ants.Count > 0)
            {
                return FnResult<DyObject>.Sucess(ants[0]);
            }
            else
            {
                return FnResult<DyObject>.Failure(Name, "NoSuchAccount");
            }

        }

        [API]
        FnResult<DyObject> FindAcnt(SOARequestContext rq)
        {
            string mp = rq.GetOrDefault<string>("mphone", "");
            var cmd = PG().Select<Account>().As("n")
                        .Where("mphone").Eq(mp)
                        .Limit(1);

            var ants = cmd.Query();
            if (ants.Count > 0)
            {
                return FnResult<DyObject>.Sucess(ants[0]);
            }
            else
            {
                return FnResult<DyObject>.Failure(Name, "NoSuchAccount");
            }
        }

        [API]
        FnResult<DyObject> NewAcnt(SOARequestContext rq)
        {
            string mphone = rq.GetOrDefault<string>("mphone", "");
            string acnt = rq.GetOrDefault<string>("acnt", "");
            string pwd = rq.GetOrDefault<string>("password", "");

            Account na = new Account();
            na.acnt = acnt;
            na.mphone = mphone;
            na.pass = pwd.IsNull() ? "" : (pwd + "_FlashQ").MD5Encrypt_normal();
            na.name = "";
            na.status = BizObjStatus.Active;
            na.headimg = "";
            na.seclv = 0;
            na.setting = "{}";

            //创建钱包
            UserAsset ua = new UserAsset();
            ua.acntid = na.id;
            ua.assettype = "Wallet";
            ua.balance = 0M;
            ua.createrid = na.id;
            ua.status = BizObjStatus.Active;

            var r = PG().Insert(na)
                       .Where("").NotExists(PG().Select<Account>("1").Where("mphone").Eq(mphone))
                       .Insert(ua)
                       .GO();
            if (r.IsPositive && r.Data != "0")
            {

                return FnResult<DyObject>.Sucess(DyObject.FromObject(na));
            }
            else
            {
                return FnResult<DyObject>.Failure(r);
            }
        }

        [API]
        FnResult<DyObject> SetPassword(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);
            string np = rq.GetOrDefault<string>("password", "");

            PG().Update<Account>().Set("pass", (np + "_FlashQ").MD5Encrypt_normal()).Where("id").Eq(uid).GO();
            return FnResult<DyObject>.Sucess(null);
        }

        [API]
        FnResult<DyObject> GetDeviceInfo(SOARequestContext rq)
        {
            string devCode = rq.GetOrDefault<string>("code", "");
            //TODO:暂时固定一个设备Code用于Demo
            devCode = "XYJ12D3C001";
            var qCmd = PG();
            qCmd.Select<Device>().Where("code").Eq(devCode)
                .Limit(1);

            qCmd.Select<Product>().As("p1").Where("optinal").Eq(false)
                .Join<Device>().As("d1").Where("code").Eq(devCode).And("id").Eq("@[p1.deviceid]");

            qCmd.Select<Product>().As("p2").Where("optinal").Eq(true)
                .Join<Device>().As("d2").Where("code").Eq(devCode).And("id").Eq("@[p2.deviceid]");

            var rtnData = qCmd.MultiQuery();

            DyObject rtnObj = rtnData[0][0];
            rtnObj.Set("Products", rtnData[1])
                  .Set("Options", rtnData[2]);

            return FnResult<DyObject>.Sucess(rtnObj);
        }

        [API]
        FnResult<DyObject> GetMyAsset(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);
            long acntid = rq.GetOrDefault<long>("acntid", -1);
            string kind = rq.GetOrDefault<string>("kind", "");
            var cmd = PG().Select<UserAsset>()
                          .Where("acntid").Eq(acntid == -1 ? uid : acntid);
            if (!kind.IsNull())
            {
                cmd.And("assettype").Eq("Wallet");
            }
            cmd.Limit(1);

            var rtn = cmd.Query();
            return FnResult<DyObject>.Sucess(DyObject.FromObjList("Assets", rtn));
        }

        /// <summary>
        /// 创建订单
        /// </summary>
        /// <param name="rq"></param>
        /// <returns></returns>
        [API]
        FnResult<DyObject> NewOrder(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);
            long devcid = rq.GetOrDefault<long>("devcid", -1);
            string products = rq.GetOrDefault<string>("products", "");
            string ordtype = rq.GetOrDefault<string>("ordtype", "Consume");

            Order od = new Order();
            od.createrid = uid;
            od.ordtype = ordtype;
            od.amount = 0.0M;
            od.status = BizObjStatus.Draft;

            ThreadPool.UnsafeQueueUserWorkItem(d =>
            {
                object[] args = (object[])d;
                string[] pids = ((string)args[0]).Split(',');
                Order ord = (Order)args[1];
                //获取商品信息
                var ps = PG().Select<Product>().As("p")
                            .Where("idstr").In(pids)
                            .LeftJoin<Device>("@.devicetype,@.model,@.providerid,@.code as devccode").As("d")
                            .Where("id").Eq("@[p.deviceid]")
                            .Query();

                var iCmd = PG();

                foreach (var p in ps)
                {
                    OrderItem oi = new OrderItem();
                    oi.productid = (long)p["id"];
                    oi.orderid = od.id;
                    oi.price = (decimal)p["price"];
                    oi.count = 1;

                    iCmd.Insert(oi);
                    od.amount += oi.price;
                }
                iCmd.Insert(ord);
                iCmd.GO();

            }, new object[2] { products, od });

            return FnResult<DyObject>.Sucess(DyObject.FromObject(od));
        }

        [API]
        FnResult<DyObject> NewPay(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);
            string uname = rq.GetOrDefault<string>("uname", "");
            string payfor = rq.GetOrDefault<string>("payfor", "");  //支付类型订单消费/充值
            long toobj = rq.GetOrDefault<long>("toobj", -1); //充值对象Order/Wallet
            decimal amount = rq.GetOrDefault<decimal>("amount", 0M);
            string channel = rq.GetOrDefault<string>("paychannel", ""); //支付通道

            PayBill pb = new PayBill();
            pb.createrid = uid;
            pb.amount = amount;
            pb.objid = toobj;
            pb.payfor = payfor;
            pb.channel = channel;
            pb.status = BizObjStatus.Draft;

            var iCmd = PG().Insert(pb);
            var rtn = iCmd.GO();

            return FnResult<DyObject>.Sucess(DyObject.FromObject(pb));
        }

        [API]
        FnResult<DyObject> PayEnd(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);
            long acntid = rq.GetOrDefault<long>("acntid", -1);
            long paybill = rq.GetOrDefault<long>("payid", -1);
            string channelId = rq.GetOrDefault<string>("openid", "");
            decimal receive = rq.GetOrDefault<decimal>("receive", 0.0m);
            DateTime ptime = rq.GetOrDefault<DateTime>("paytime", DateTime.Now);
            string payfor = rq.GetOrDefault<string>("payfor", "");
            long payobj = rq.GetOrDefault<long>("payobj", -1);
            bool deduct = rq.GetOrDefault<bool>("deduct", false);

            var uCmd = PG();
            uCmd.Select<PayBill>("1")
                .Where("id").Eq(paybill)
                .And("status").Eq(BizObjStatus.Draft)
                .ForUpdate()
                .Update<PayBill>().As("pb")
                .Set("status", BizObjStatus.Completed)
                .Set("received", receive)
                .Set("channelid", channelId)
                .Set("updatetime", ptime)
                .Where("id").Eq(paybill)
                .And("status").Eq(BizObjStatus.Draft);

            if (!deduct && receive > 0)
            {
                /*不需要扣减钱包余额*/
                if (payfor == "Wallet")
                {
                    uCmd.Update<UserAsset>()
                        .Set("balance", new VarReference() { Exp = "balance+" + receive })
                        .Where("id").Eq(payobj);
                }
                else if (payfor == "Order")
                {
                    uCmd.Update<Order>()
                        .Set("status", BizObjStatus.Active)
                        .Where("id").Eq(payobj)
                        .And("amount").SmallerOrEq(receive);
                }
            }
            else
            {
                /*扣减钱包余额*/
                var dCmd = PG().Select<UserAsset>("1")
                    .Where("acntid").Eq(uid == -1 ? acntid : uid)
                    .ForUpdate()
                    .Update<UserAsset>()
                    .Set("balance", new VarReference() { Exp = "balance-" + receive })
                    .Where("acntid").Eq(uid == -1 ? acntid : uid)
                    .And("assettype").Eq("Wallet")
                    .And("balance").BiggerOrEq(receive)
                    .GO();

                if (!dCmd.IsPositive || dCmd.Data == "0")
                {
                    return FnResult<DyObject>.Failure("System", "PayFail");
                }

                if (payfor == "Order")
                {
                    uCmd.Update<Order>()
                        .Set("status", BizObjStatus.Active)
                        .Where("id").Eq(payobj)
                        .And("amount").SmallerOrEq(receive);
                }
            }

            var r = uCmd.GO();
            if (r.IsPositive && r.Data != "0")
            {
                return FnResult<DyObject>.Sucess(null);
            }
            else
            {
                return FnResult<DyObject>.Failure("System", "PayFail");
            }
        }

        [API]
        FnResult<DyObject> QueryOrders(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);

            var qCmd = PG().Select<Order>().As("od")
                           .Where("createrid").Eq(uid);
            var rtn = qCmd.Query();

            return FnResult<DyObject>.Sucess(DyObject.FromObjList("Orders", rtn));
        }

        [API]
        FnResult<DyObject> Ini(SOARequestContext rq)
        {
            var iCmd = PG();

            Device d = new Device();
            d.siteid = -1;
            d.code = "XYJ12D3C001";
            d.devicetype = "洗衣机";
            d.model = "45KG";
            d.status = BizObjStatus.Draft;
            iCmd.Insert(d);

            Product[] ps = new Product[4];
            ps[0] = new Product()
            {
                deviceid = d.id,
                optinal = false,
                name = "快速洗",
                unit = "分钟",
                quantity = 25,
                price = 3.5M
            };
            ps[1] = new Product()
            {
                deviceid = d.id,
                optinal = false,
                name = "标准洗",
                unit = "分钟",
                quantity = 45,
                price = 4.5M
            };
            ps[2] = new Product()
            {
                deviceid = d.id,
                optinal = false,
                name = "大物洗",
                unit = "分钟",
                quantity = 55,
                price = 5.5M
            };
            ps[3] = new Product()
            {
                deviceid = d.id,
                optinal = false,
                name = "单脱水",
                unit = "分钟",
                quantity = 7,
                price = 1M
            };
            foreach (var p in ps)
            {
                iCmd.Insert(p);
            }

            Product[] ops = new Product[2];
            ops[0] = new Product()
            {
                deviceid = d.id,
                optinal = true,
                name = "桶清洁",
                unit = "分钟",
                quantity = 2,
                price = 0
            };
            ops[1] = new Product()
            {
                deviceid = d.id,
                optinal = true,
                name = "加温洗",
                unit = "分钟",
                quantity = 15,
                price = 1M
            };
            foreach (var op in ops)
            {
                iCmd.Insert(op);
            }
            iCmd.GO();
            return FnResult<DyObject>.Sucess(null);
        }

        [API()]
        /// <summary>
        /// 获取与指定业务对象关联的业务日志
        /// </summary>
        /// <param name="rq"></param>
        /// <returns></returns>
        FnResult<DyObject> GetObjLogs(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);
            long objid = rq.GetOrDefault<long>("objid", -1);
            long refobjid = rq.GetOrDefault<long>("refobjid", -1);
            long last = rq.GetOrDefault<long>("lastid", -1);

            var qCmd = PG().Select<BizLog>().As("b")
                        .Where("objid").Eq(objid)
                        .And("id").Bigger(last)
                        .AscBy("id");

            if (refobjid > 0)
            {
                qCmd.And("refobjid").Eq(refobjid);
            }

            qCmd.LeftJoin<Account>("@.name as opr_acnt_name,@.headimg as opr_headimg,@.mphone as opr_mphone").As("n")
                .Where("id").Eq("@[b.oprid]")
                .LeftJoin<Company>("@.name as company_name").As("g")
                .Where("id").Eq("@[b.oprcompanyid]");

            return FnResult<DyObject>.Sucess(DyObject.FromObjList("Logs", qCmd.Query()));
        }

        [API()]
        FnResult<DyObject> Log(SOARequestContext rq)
        {
            long uid = rq.GetOrDefault<long>("uid", -1);
            long oprid = rq.GetOrDefault<long>("oprid", -1);
            string oprname = rq.GetOrDefault<string>("oprname", String.Empty);
            long opr_companyid = rq.GetOrDefault<long>("companyid", -1);
            long bizobjid = rq.GetOrDefault<long>("bizobjid", -1);
            long refobjid = rq.GetOrDefault<long>("refobjid", -1);
            string app = rq.GetOrDefault<string>("app", string.Empty);
            string action = rq.GetOrDefault<string>("bizaction", string.Empty);
            DyObject memo = rq.GetOrDefault<DyObject>("memo", new DyObject());
            bool ispositive = rq.GetOrDefault<bool>("ispositive", false);
            bool islast = rq.GetOrDefault<bool>("islast", false);

            Log(app, oprid, opr_companyid, action, bizobjid, refobjid, memo, ispositive, oprname, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), islast);

            return FnResult<DyObject>.Sucess(null);
        }

        /// <summary>
        /// 记录一条业务操作日志
        /// </summary>
        /// <param name="app">日志写入的来源业务单元</param>
        /// <param name="opr_id">操作人帐号ID</param>
        /// <param name="opr_cpny_id">操作人所属公司[如果传-1，则尝试根据操作人帐号获取]</param>
        /// <param name="action">动作</param>
        /// <param name="objid">操作对象ID</param>
        /// <param name="objinfo">操作对象的描述信息</param>
        /// <param name="ref_objid">相关对象ID【适用于通过操作相关对象来影响目标对象的情况】</param>
        /// <param name="ref_objinfo">相关对象的描述信息</param>
        /// <param name="memo">其他描述信息</param>
        /// <param name="ispositive">操作的性质</param>
        /// <param name="opr_name">操作人姓名[如果确定提供了opr_id，可以不提供]</param>
        public void Log(string app, long opr_id, long opr_cpny_id, string action, long objid, long ref_objid = -1, DyObject memo = null, bool ispositive = true, string opr_name = "", string ctime = "", bool islast = false, long fixid = -1)
        {
            BizLog blg = new BizLog();
            blg.app = app;
            blg.oprname = opr_name;
            blg.oprid = opr_id;
            blg.oprcompanyid = opr_cpny_id;
            blg.objid = objid;
            blg.action = action;
            blg.refobjid = ref_objid;
            blg.memo = memo != null ? memo.FastJson() : "{}";
            blg.ispositive = ispositive;
            blg.islast = islast;
            if (fixid != -1)
            {
                blg.id = fixid;
                blg.idstr = fixid.ToString();
            }
            if (!ctime.IsNull())
            {
                DateTime.TryParse(ctime, out blg.createtime);
            }

            ThreadPool.UnsafeQueueUserWorkItem((l) =>
            {
                BizLog g = l as BizLog;

                if (!g.islast)
                {
                    PG().Insert(g)
                        .GO();
                }
                else
                {
                    PG().Update<BizLog>()
                        .Set("islast", false)
                        .Where("islast").Eq(true)
                        .And("objid").Eq(g.objid)
                        .And("action").Eq(g.action)
                        .And("refobjid").Eq(g.refobjid)
                        .Insert(blg)
                        .GO();
                }

            }, blg);
        }

        /// <summary>
        /// 记录一条业务操作日志
        /// </summary>
        /// <param name="cmd">当前使用的数据库操作指令</param>
        /// <param name="opr_id">操作人帐号ID</param>
        /// <param name="opr_cpny_id">操作人所属公司[如果传-1，则尝试根据操作人帐号获取]</param>
        /// <param name="action">动作</param>
        /// <param name="objid">操作对象ID</param>
        /// <param name="objinfo">操作对象的描述信息</param>
        /// <param name="ref_objid">相关对象ID【适用于通过操作相关对象来影响目标对象的情况】</param>
        /// <param name="ref_objinfo">相关对象的描述信息</param>
        /// <param name="memo">其他描述信息</param>
        /// <param name="ispositive">操作的性质</param>
        /// <param name="opr_name">操作人姓名[如果确定提供了opr_id，可以不提供]</param>
        public void Log(ref PGSQLHelper cmd, string app, long opr_id, long opr_cpny_id, string action, long objid, long ref_objid = -1, DyObject memo = null, bool ispositive = true, string opr_name = "", string ctime = "", bool islast = false, long fixid = -1)
        {
            if (cmd == null)
            {
                Log(app, opr_id, opr_cpny_id, action, objid, ref_objid, memo, ispositive, opr_name, ctime, islast);
            }
            else
            {
                BizLog blg = new BizLog();
                blg.app = app;
                blg.oprname = opr_name;
                blg.oprid = opr_id;
                blg.oprcompanyid = opr_cpny_id;
                blg.objid = objid;
                blg.action = action;
                blg.refobjid = ref_objid;
                blg.memo = memo == null ? "{}" : memo.FastJson();
                blg.ispositive = ispositive;
                blg.islast = islast;
                if (fixid != -1)
                {
                    blg.id = fixid;
                    blg.idstr = fixid.ToString();
                }
                if (!ctime.IsNull())
                {
                    DateTime.TryParse(ctime, out blg.createtime);
                }

                if (islast)
                {
                    cmd.Update<BizLog>()
                        .Set("islast", false)
                        .Where("islast").Eq(true)
                        .And("objid").Eq(blg.objid)
                        .And("action").Eq(blg.action)
                        .And("refobjid").Eq(blg.refobjid);
                }

                cmd.Insert(blg);
            }
        }

    }

    #region 帐号相关
    /// <summary>
    /// 帐号信息
    /// </summary>
    public class Account : BizObj
    {
        public string acnt;     //用于登录的帐号【也即手机号】
        public string pass;     //登录密码
        public string name;     //注册姓名【如实名认证，则更新为实名】
        public string mphone;   //绑定的主手机号
        public string email;    //默认邮箱地址
        public string headimg;  //头像图片地址
        public short seclv;     //安全级别[平台管理员为5-9，普通用户0-4]
        public string setting;  //用户个性配置
    }

    /// <summary>
    /// 用户在其他途径中获得的唯一性标识
    /// </summary>
    public class OpenAcnt : BizObj
    {
        /// <summary>
        /// 绑定的信息友好标识【可选】如微信用户名
        /// </summary>
        public string openname;

        /// <summary>
        /// 绑定来源WeChat,MPhone
        /// </summary>
        public string opensrc;

        /// <summary>
        /// 绑定的信息标识，可唯一识别一个人，如手机号，可用于匹配唯一的帐号
        /// </summary>
        public string openid;

        public long acntid;
    }

    /// <summary>
    /// 经过认证的真实身份
    /// </summary>
    public class People : BizObj
    {
        public string socialid;  //身份证号码
        public string realname;  //真实姓名
        public char firstletter;
        public string fullpinyin;
        public string idcardpic;
        public string approveresult; //最终认证结果描述
    }

    /// <summary>
    /// 用户资产
    /// </summary>
    public class UserAsset : BizObj
    {
        public long acntid;
        public string assettype;
        public decimal balance;
    }
    #endregion

    #region 设备相关
    /// <summary>
    /// 场地信息
    /// </summary>
    public class SvcSite : BizObj
    {
        public string title;
    }

    /// <summary>
    /// 设备
    /// </summary>
    public class Device : BizObj
    {
        public long siteid;

        public string devicetype;

        public string model;

        /// <summary>
        /// 供应商ID-关联Company对象
        /// </summary>
        public long providerid;

        public string code;
    }

    public class Product : BizObj
    {
        public long deviceid;

        /// <summary>
        /// 区分产品性质（主产品/额外附加项）
        /// </summary>
        public bool optinal;

        public string name;

        public string unit;

        public int quantity;

        public decimal price;

        public string code;
    }

    /// <summary>
    /// 公司（供应商、服务商、甲方）
    /// </summary>
    public class Company : BizObj
    {
        public string name;
    }

    #endregion

    #region 交易相关
    public class Order : BizObj
    {
        public long providerid;
        public string ordtype;
        public decimal amount;
    }

    public class OrderItem : BizObjItem
    {
        public long orderid;
        public long productid;
        public decimal price;
        public decimal count;
    }

    public class PayBill : BizObj
    {
        public string payfor;
        public long objid;
        public decimal amount;  //应收
        public decimal received; //实收
        public string channel;  //支付途径
        public string channelid;  //支付途径分配的记录ID
    }
    #endregion

    /// <summary>
    /// 业务日志
    /// </summary>
    public class BizLog
    {
        public BizLog()
        {
            id = IdFactory.NewID();
            createtime = DateTime.Now;
            updatetime = DateTime.Now;
            memo = "{}";
        }

        public long id;

        public string idstr
        {
            get { return id.ToString(); }
            set { }
        }

        public DateTime createtime;

        public DateTime updatetime;

        public string app;

        public string oprname;

        public long oprid;

        public long oprcompanyid;

        public string action;

        public long objid;

        public long refobjid;

        public string refobjidstr
        {
            get { return refobjid.ToString(); }
            set { }
        }

        public string memo;

        public bool ispositive;

        /// <summary>
        /// 对于使用非覆盖模式的日志，标记最后一条相似操作
        /// </summary>
        public bool islast;
    }
}
